package repo

import (
	"context"
	"database/sql"
	"errors"
	"github.com/star-find-cloud/star-chat/group/domain"
	"github.com/star-find-cloud/star-chat/pkg/database"
	log "github.com/star-find-cloud/star-chat/pkg/logger"
)

type GroupRepoImpl struct {
	db    database.Database
	mongo *database.Mongo
	cache *database.Redis
}

func NewGroupRepoImpl(db database.Database, mongo *database.Mongo, cache *database.Redis) *GroupRepoImpl {
	return &GroupRepoImpl{
		db:    db,
		mongo: mongo,
		cache: cache,
	}
}

func (r *GroupRepoImpl) Create(ctx context.Context, group *domain.Group) (int64, error) {
	sqlStr := "insert into star_chat.public.groups(id, name, master_id, admin_id, users_id, users_count, max_count, avatar, qr_code_id, status) values (?,?,?,?,?,?,?,?,?);"

	result, err := r.db.GetDB().ExecContext(ctx, sqlStr,
		group.ID,
		group.Name,
		group.MasterID,
		group.UsersID,
		len(group.UsersID),
		group.MaxCount,
		group.Avatar,
		group.QRcodeID,
		group.Status,
	)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Errorf("Database insert group error: %v", err)
			return 0, err
		}
		log.AppLogger.Errorf("an error occurred in the group repo, err: %v", err)
		return 0, err
	}
	id, err := result.LastInsertId()

	return id, nil
}

func (r *GroupRepoImpl) Get(ctx context.Context, id int64) (*domain.Group, error) {
	sqlStr := "select name, master_id, admin_id, users_id, users_count, max_count, avatar, qr_code_id, status, created_at from star_chat.public.groups where id = ?"
	group := &Group{}

	err := r.db.GetDB().GetContext(ctx, group, sqlStr, id)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Errorf("Database get group matedata error: %v", err)
			return nil, err
		}
		log.AppLogger.Errorf("an error occurred in the group repo, err: %v", err)
		return nil, err
	}
	return &domain.Group{
		ID:         group.ID,
		Name:       group.Name,
		MasterID:   group.MasterID,
		UsersID:    group.UsersID,
		UsersCount: group.UsersCount,
		MaxCount:   group.MaxCount,
		Avatar:     group.Avatar,
		QRcodeID:   group.QRcodeID,
		Status:     group.Status,
		CreatedAt:  group.CreatedAt.Unix(),
	}, nil
}

func (r *GroupRepoImpl) GetByMasterID(ctx context.Context, masterID int64) ([]*domain.Group, error) {
	sqlStr := "select id, name, admin_id, users_id, users_count, max_count, avatar, qr_code_id, status, created_at from star_chat.public.groups where master_id = ?"
	var groups []Group

	err := r.db.GetDB().SelectContext(ctx, &groups, sqlStr, masterID)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Errorf("Database get group matedata error: %v", err)
			return nil, err
		}
		log.AppLogger.Errorf("an error occurred in the group repo, err: %v", err)
		return nil, err
	}

	var domainGroups []*domain.Group
	for _, group := range groups {
		domainGroup := &domain.Group{
			ID:         group.ID,
			Name:       group.Name,
			MasterID:   group.MasterID,
			UsersID:    group.UsersID,
			UsersCount: group.UsersCount,
			MaxCount:   group.MaxCount,
			Avatar:     group.Avatar,
			QRcodeID:   group.QRcodeID,
			Status:     group.Status,
			CreatedAt:  group.CreatedAt.Unix(),
		}
		domainGroups = append(domainGroups, domainGroup)
	}
	return domainGroups, err
}

func (r *GroupRepoImpl) GetByName(ctx context.Context, name string) ([]*domain.Group, error) {
	sqlStr := "select * from star_chat.public.groups where name like ?"
	var groups []Group

	err := r.db.GetDB().SelectContext(ctx, &groups, sqlStr, name)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Errorf("Database get group matedata error: %v", err)
			return nil, err
		}
		log.AppLogger.Errorf("an error occurred in the group repo, err: %v", err)
		return nil, err
	}
	var domainGroups []*domain.Group
	for _, group := range groups {
		domainGroup := &domain.Group{
			ID:         group.ID,
			Name:       group.Name,
			MasterID:   group.MasterID,
			UsersID:    group.UsersID,
			UsersCount: group.UsersCount,
			MaxCount:   group.MaxCount,
			Avatar:     group.Avatar,
			QRcodeID:   group.QRcodeID,
			Status:     group.Status,
			CreatedAt:  group.CreatedAt.Unix(),
		}
		domainGroups = append(domainGroups, domainGroup)
	}
	return domainGroups, nil
}

func (r *GroupRepoImpl) Update(ctx context.Context, group *domain.Group) error {
	sqlStr := "update star_chat.public.groups set name=?, master_id=?,max_count=?,avatar=?,qr_code_id=?, status=? where id = ?"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr,
		group.Name,
		group.MasterID,
		group.MaxCount,
		group.Avatar,
		group.QRcodeID,
		group.Status,
		group.ID,
	)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Errorf("Database update group matedata error: %v", err)
			return err
		}
		log.AppLogger.Errorf("an error occurred in the group repo, err: %v", err)
		return err
	}
	return nil
}

func (r *GroupRepoImpl) UpdateMaster(ctx context.Context, newMasterID, id int64) error {
	sqlStr := "update star_chat.public.groups set master_id=? where id = ?"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr, newMasterID, id)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Errorf("Database update group matedata error: %v", err)
			return err
		}
		log.AppLogger.Errorf("an error occurred in the group repo, err: %v", err)
		return err
	}
	return nil
}

func (r *GroupRepoImpl) UpdateName(ctx context.Context, id int64, name string) error {
	sqlStr := "update star_chat.public.groups set name=? where id = ?"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr, name, id)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Errorf("Database update group matedata error: %v", err)
			return err
		}
		log.AppLogger.Errorf("an error occurred in the group repo, err: %v", err)
		return err
	}
	return nil
}

func (r *GroupRepoImpl) UpdateAvatar(ctx context.Context, id int64, avatar int64) error {
	sqlStr := "update star_chat.public.groups set avatar=? where id = ?"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr, avatar, id)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Errorf("Database update group matedata error: %v", err)
			return err
		}
		log.AppLogger.Errorf("an error occurred in the group repo, err: %v", err)
		return err
	}
	return nil
}

func (r *GroupRepoImpl) UpdateStatus(ctx context.Context, id int64, status int64) error {
	sqlStr := "update star_chat.public.groups set status=? where id = ?"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr, status, id)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Errorf("Database update group matedata error: %v", err)
			return err
		}
		log.AppLogger.Errorf("an error occurred in the group repo, err: %v", err)
		return err
	}
	return nil
}

func (r *GroupRepoImpl) AddUser(ctx context.Context, groupID, userID int64) error {
	sqlStr := "update star_chat.public.groups set users_id=array_append(users_id, ?) where id = ?"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr, userID, groupID)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Errorf("Database update group matedata error: %v", err)
			return err
		}
		log.AppLogger.Errorf("an error occurred in the group repo, err: %v", err)
		return err
	}
	return nil
}

func (r *GroupRepoImpl) AddAdmin(ctx context.Context, groupID, userID int64) error {
	sqlStr := "update star_chat.public.groups set admin_id=array_append(admin_id, ?) where id = ?;"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr, userID, groupID)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Errorf("Database update group matedata error: %v", err)
			return err
		}
		log.AppLogger.Errorf("an error occurred in the group repo, err: %v", err)
		return err
	}
	return nil
}

func (r *GroupRepoImpl) Delete(ctx context.Context, id int64) error {
	sqlStr := "update star_chat.public.groups set status = 'Deleted' where id = ?"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr, id)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Errorf("Database update group matedata error: %v", err)
			return err
		}
		log.AppLogger.Errorf("an error occurred in the group repo, err: %v", err)
		return err
	}
	return nil
}

func (r *GroupRepoImpl) Recover(ctx context.Context, id int64) error {
	sqlStr := "update star_chat.public.groups set status = 'Normal' where id = ?"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr, id)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Errorf("Database update group matedata error: %v", err)
			return err
		}
		log.AppLogger.Errorf("an error occurred in the group repo, err: %v", err)
		return err
	}
	return nil
}
